/**
 * message type enum
 */
var MessageType = {
    /**
     * Text message
     */
    Text: 'Text',
    /**
     * Data message
     */
    Data: 'Data',
    /**
     * Notice message
     */
    Notice: 'Notice',
    /**
     * Alarm message
     */
    Alarm: 'Alarm',
    /**
     * Ack message
     */
    Ack: 'Ack',
    /**
     * Heartbeat message
     */
    Heartbeat: 'Heartbeat',
    /**
     * Other message
     */
    Other: 'Other'
}

class MessageClient {
    constructor(server, clientId) {
        this.server = server;
        this.clientId = clientId;
        this.url = this.server + '?clientId=' + this.clientId;

        if (this.clientId) {
            this.connection = new signalR.HubConnectionBuilder()
                .withUrl(this.url, {
                    skipNegotiation: true,
                    transport: signalR.HttpTransportType.WebSockets,
                })
                .build();

            this.connection.onclose(this.start);
        }
    }

    start() {
        if (this.connection) {
            if (this.connection.state == signalR.HubConnectionState.Disconnected) {
                this.connection.start(() => console.log(`eis message channel ${this.url} started!`)).catch((err) => console.error(err));
            }
        } else {
            console.info('no message connection!');
        }

        // 每隔10s重新连接
        setTimeout(() => this.start(), 10000);
    }

    /**
     * 调用服务器 Send 方法, 发送内容给给服务器
     * @param {string} content 发送的内容
     * @param {string} receiver 接收者
     * @param {string} messageType 消息类型, 默认 文本消息
     * @param {function} success 发送完成后执行的方法
     * @param {function} error 发送失败后执行的方法
     */
    send(content, receiver, messageType, success, error) {
        var message = {
            sender: this.clientId,
            sendTime: new Date(),
            receiver: receiver,
            type: messageType || MessageType.Text,
            content: content,
        };

        this.connection
            .invoke('Send', message)
            .then(() => {
                console.log(`eis message channel '${this.clientId}' send "${message.content}" to ${receiver} at ${message.sendTime}!`);
                if (success && typeof success === 'function') {
                    return success(message);
                }
            })
            .catch((err) => {
                console.error(err);
                if (error && typeof error === 'function') {
                    return error(message);
                }
            });
    }

    /**
     * 下发通知
     * @param {Message} message 发送的内容
     */
    ack(message) {
        if (message.receiver !== this.clientId) {
            console.error(`The receiver of message "${message.content}" is not current client!`);
        }

        this.connection
            .invoke('Ack', {
                id: message.messageId,
                sender: message.sender,
                sendTime: message.sendTime,
                receiver: message.receiver,
                receiveTime: new Date(),
                type: MessageType.Ack,
                content: message.content
            })
            .then(() => {
                console.log(`eis message channel '${this.clientId}' send ACK to '${message.sender}'!`);
            })
            .catch((err) => {
                console.error(err);
            });
    }

    /**
     * 下发通知
     * @param {string} content 发送的内容
     * @param {function} success 发送完成后执行的方法
     * @param {function} error 发送失败后执行的方法
     */
    notice(content, success, error) {
        return this.send(content, null, MessageType.Notice, success, error);
    }

    /**
     * 发送预警消息
     * @param {string} content 发送的内容
     * @param {string} receiver 接收者
     * @param {function} success 发送完成后执行的方法
     * @param {function} error 发送失败后执行的方法
     */
    alarm(content, receiver, success, error) {
        return this.send(content, receiver, MessageType.Alarm, success, error);
    }

    /**
     * 注册客户端方法供服务器调用
     * @param {string} method 方法名
     * @param {function} callback 调用成功之后执行的方法
     */
    on(method, callback) {
        this.connection.off(method);
        this.connection.on(method, (message) => {
            switch (message.messageType) {
                case MessageType.Text:
                case MessageType.Data:
                case MessageType.Notice:
                case MessageType.Alarm:
                    if (message.sender != this.clientId) {
                        this.ack(message);
                    }
                    break;
                default:
                    break;
            }

            if (callback && typeof callback === 'function') {
                return callback(message);
            }
        });
    }
}